using System.Xml;
using System.Xml.Serialization;
using NalogGarLoader.Model.GarRepositoryReader;
using System.Diagnostics;
using LinqToDB;
using NalogGarLoader.Data;
using LinqToDB.Data;
using System.Collections;
using System.Collections.Concurrent;
using NalogGarLoader.Data.Tables.GarEntries;

namespace NalogGarLoader.Model;

public class GarFileBulkImporter : GarFileImporter
{
    private static ConcurrentDictionary<string, Type> ListGenericTypeCache { get; } = new();    

    private IList InsertBuffer { get; set; }
    private BulkCopyOptions BulkCopyOptions { get; init; }
    public int BulkInsertCount { get; init; }
    public GarFileBulkImporter(GarRepositoryEntry repositoryEntry, int bulkInsertCount)
        : base(repositoryEntry)
    {
        if (bulkInsertCount < 1)
            throw new ArgumentException(nameof(bulkInsertCount));

        this.BulkInsertCount = bulkInsertCount;

        this.BulkCopyOptions = new()
        {
            KeepIdentity = true,
            BulkCopyTimeout = 60
        };

        this.InsertBuffer = this.CreateInsertBuffer();
    }

    protected IList CreateInsertBuffer()
    {
        if (!ListGenericTypeCache.TryGetValue(this.GarEntryType.FullName!, out Type? genericListType))
        {
            genericListType = typeof(List<>).MakeGenericType(this.GarEntryType);
            ListGenericTypeCache.TryAdd(this.GarEntryType.FullName!, genericListType);
        }

        return (IList)Activator.CreateInstance(genericListType, string.IsNullOrEmpty(this.RepositoryEntry.StateId) ? 30 : this.BulkInsertCount / 2)!;
    }

    protected override void OnEndImport() => this.DoInsert();

    protected override void InsertEntry(GarEntryBase garEntry)
    {
        this.InsertBuffer.Add(garEntry);

        if (this.InsertBuffer.Count >= this.BulkInsertCount)
            this.DoInsert();
    }

    protected void DoInsert()
    {
        if (this.InsertBuffer.Count > 0)
        {
            this.GarDbContext.BulkCopy(tableType: this.GarEntryType, source: this.InsertBuffer, options: this.BulkCopyOptions);
            this.InsertBuffer = this.CreateInsertBuffer();
        }
    }
}